/* e100_open.c - e100_oepn, e100_enable_irq, e100_disable_irq,
 * 				e100_exec_cmd, e100_exec_cb*/

#include <xinu.h>

/* allocate descriptor ring statically */
local 	status 	e100_configure(struct ether *ethptr, struct e100_tx_desc *cb);
local 	status 	e100_setup_iaaddr(struct ether *ethptr, struct e100_tx_desc *cb);
local 	status 	e100_init_hw(struct ether *ethptr);
local 	status 	e100_dump(struct ether *ethptr, struct e100_tx_desc *cb);

/*------------------------------------------------------------------------
 * e100_open - allocate resources and prepare hardware for transmit and 
 *                receive
 *------------------------------------------------------------------------
 */
status e100_open(
	struct 	ether 	*ethptr
	)
{
	int i;
	struct e100_rx_desc *rxRingptr;
	struct e100_tx_desc *txRingptr;
	
	/* Initialize structure pointers */
	
	ethptr->rxRingSize = E100_RX_RING_SIZE;
	ethptr->txRingSize = E100_TX_RING_SIZE;
	ethptr->isem = semcreate(0);
	ethptr->osem = semcreate(ethptr->txRingSize);
	kprintf("In open, ethptr->osem = %d\r\n", ethptr->osem);
	
	/* Rings must be aligned on a 16-byte boundary */
	
	ethptr->rxRing = (void *)getmem(((ethptr->rxRingSize)+1)*E100_RDSIZE);
	ethptr->txRing = (void *)getmem(((ethptr->txRingSize)+1)*E100_TDSIZE);
	//ethptr->txHead = ethptr->txTail = 0;
	//ethptr->rxHead = ethptr->rxTail = 0;
	ethptr->rxRing = (void *)(((uint32)ethptr->rxRing + 0xf) & ~0xf);
	ethptr->txRing = (void *)(((uint32)ethptr->txRing + 0xf) & ~0xf);
	
	/* Buffers are highly recommended to be allocated on cache-line */
	/* 	size (64-byte for E8400) 				*/
	
	/*ethptr->rxBufs = (void *)getmem((ethptr->rxRingSize + 1) 
									* ETH_BUF_SIZE);
    ethptr->txBufs = (void *)getmem((ethptr->txRingSize + 1) 
									* ETH_BUF_SIZE);
	ethptr->rxBufs = (void *)(((uint32)ethptr->rxBufs + 0x3f) 
							  & ~0x3f);
	ethptr->txBufs = (void *)(((uint32)ethptr->txBufs + 0x3f) 
							  & ~0x3f);
	*/
	/*if ( (SYSERR == (uint32)ethptr->rxBufs) || 
		(SYSERR == (uint32)ethptr->txBufs) ) {
		return SYSERR;
	}*/
	
	/* Set buffer pointers and rings to zero */
	
	//memset(ethptr->rxBufs, '\0', ethptr->rxRingSize * ETH_BUF_SIZE);
	//memset(ethptr->txBufs, '\0', ethptr->txRingSize * ETH_BUF_SIZE);
	memset(ethptr->rxRing, '\0', E100_RDSIZE * ethptr->rxRingSize+1);
	memset(ethptr->txRing, '\0', E100_TDSIZE * ethptr->txRingSize+1);
	
	/* Insert the buffer into descriptor ring */
	
	rxRingptr = (struct e100_rx_desc *)ethptr->rxRing;
	struct e100_rx_desc * temp1 = rxRingptr;
	//bufptr = (uint32)ethptr->rxBufs;
	for (i = 0; i < ethptr->rxRingSize-1; i++) {
		rxRingptr->size = ETH_BUF_SIZE;
		rxRingptr->link = (uint32)(rxRingptr)+E100_RDSIZE;
		rxRingptr++;
	}
	rxRingptr->size = ETH_BUF_SIZE;
	rxRingptr->link = (uint32)temp1;
	txRingptr = (struct e100_tx_desc *)ethptr->txRing;

	struct e100_tx_desc * temp2 = txRingptr;
	//bufptr = (uint32)ethptr->txBufs;
	for (i = 0; i < ethptr->txRingSize-1; i++) {
		txRingptr->link = (uint32)(txRingptr)+E100_TDSIZE;
		txRingptr++;
	}
	txRingptr->link = (uint32)temp2;
	
	/* Initialize the hardware	*/
	if (OK != e100_init_hw(ethptr)) {
		kprintf("e100_init_hw failed\r\n");
		return SYSERR;
	}
	
	set_evec(ethptr->dev->dvirq + IRQBASE, (uint32)ethDispatch);	
	e100_enable_irq(ethptr);
	
	return OK;
}

/*------------------------------------------------------------------------
 * e100_configure - configure RU and CU after reset
 *------------------------------------------------------------------------
 */
local status e100_configure(
	struct 	ether *ethptr, 
	struct e100_tx_desc *cb
	)
{
	struct config *configure = &cb->u.configure;
	
	cb->command = cb_config;
	
	memset(configure, 0, sizeof(struct config));
	
	configure->byte_count = 0x16;				/* bytes in this struct */
	configure->rx_fifo_limit = 0x8;			/* bytes in FIFO before DMA */
	configure->direct_rx_dma = 0x1;			/* reserved */
	configure->standard_tcb = 0x1;				/* 1=standard, 0=extended */
	configure->standard_stat_counter = 0x1;	/* 1=standard, 0=extended */
	configure->rx_discard_short_frames = 0x1;	/* 1=discard, 0=pass */
	configure->tx_underrun_retry = 0x3;		/* # of underrun retries */
	//if (e100_phy_supports_mii(nic))
		//configure->mii_mode = 1;          		/* 1=MII mode, 0=i82503 mode */
	configure->pad10 = 0x6;
	configure->no_source_addr_insertion = 0x1;	/* 1=no, 0=yes */
	configure->preamble_length = 0x2;			/* 0=1, 1=3, 2=7, 3=15 bytes */
	configure->ifs = 0x6;						/* x16 = inter frame spacing */
	configure->ip_addr_hi = 0xF2;				/* ARP IP filter - not used */
	configure->pad15_1 = 0x1;
	configure->pad15_2 = 0x1;
	configure->crs_or_cdt = 0x0;				/* 0=CRS only, 1=CRS or CDT */
	configure->fc_delay_hi = 0x40;				/* time delay for fc frame */
	configure->tx_padding = 0x1;				/* 1=pad short frames */
	configure->fc_priority_threshold = 0x7;	/* 7=priority fc disabled */
	configure->pad18 = 0x1;
	configure->full_duplex_pin = 0x1;			/* 1=examine FDX# pin */
	configure->pad20_1 = 0x1F;
	configure->fc_priority_location = 0x1;		/* 1=byte#31, 0=byte#19 */
	configure->pad21_1 = 0x5;
	
	return OK;
}

/*------------------------------------------------------------------------
 * e100_setup_iaaddr - setup indivitual address
 *------------------------------------------------------------------------
 */
 
local status e100_setup_iaaddr(
	struct 	ether *ethptr,
	struct e100_tx_desc *cb
	)
{
	cb->command = cb_iaaddr;
	memcpy(cb->u.macaddr, ethptr->devAddress, ETH_ADDR_LEN);
	return OK;
}

/*------------------------------------------------------------------------
 * e100_dump - dump register content to memory
 *------------------------------------------------------------------------
 */
 
local status e100_dump(
	struct 	ether *ethptr,
	struct e100_tx_desc *cb
	)
{
	cb->command = cb_dump;
	cb->u.dump_buffer_addr = (uint32)(dump_buffer);
	return OK;
}

/*------------------------------------------------------------------------
 * e100_init_hw - Initialize the hardware
 *------------------------------------------------------------------------
 */
local status e100_init_hw(
	struct 	ether *ethptr
	)
{
	dump_buffer = getmem(596);
	memset(dump_buffer, '\0', 596); 

	/* Load CUC and RUC base address	*/
	if ((e100_exec_cmd(ethptr, cuc_load_base, 0)) != OK) {
		kprintf("cuc_load_base ERROR\r\n");
		return SYSERR;
	}
	if (e100_exec_cmd(ethptr, ruc_load_base, 0) != OK) {
		kprintf("ruc_load_base ERROR\r\n");
		return SYSERR;
	}
	kprintf("Before starting CU and RU, SCB Status = %2x\r\n", inb(ethptr->iobase + E100_STATUS));
	/* CUC and RUC start	*/
	if (e100_exec_cmd(ethptr, cuc_start, (uint32)ethptr->txRing) != OK) {
		kprintf("cuc_start ERROR\r\n");
		return SYSERR;
	}
	if (e100_exec_cmd(ethptr, ruc_start, (uint32)ethptr->rxRing) != OK) {
		kprintf("ruc_start ERROR\r\n");
		return SYSERR;
	}
	DELAY(2000);
	kprintf("After starting CU and RU, SCB Status = %2x\r\n", inb(ethptr->iobase + E100_STATUS));
	/* Configure the hardware for Rx and Tx */
	/* if (e100_exec_cb(ethptr, e100_configure) != OK) {
		kprintf("e100_configure ERROR\r\n");
		return SYSERR;
	} */
	
	/* Setup indivitual address	*/
	/* if (e100_exec_cb(ethptr, e100_setup_iaaddr) != OK) {
		kprintf("e100_setup_iaaddr ERROR\r\n");
		return SYSERR;
	} */

	/*Dump register content to memory	*/
	if (e100_exec_cb(ethptr, e100_dump) != OK) {
		kprintf("e100_dump ERROR\r\n");
		return SYSERR;
	}

	/* Print register content	*/
    kprintf("dump_buffer = \r\n");
	kprintf("Individual Address Register 1 = %02x\r\n", 0xff&dump_buffer[39]);
	kprintf("Individual Address Register 2 = %02x\r\n", 0xff&dump_buffer[40]);
	kprintf("Individual Address Register 3 = %02x\r\n", 0xff&dump_buffer[41]);
	kprintf("Individual Address Register 4 = %02x\r\n", 0xff&dump_buffer[42]);
	kprintf("Individual Address Register 5 = %02x\r\n", 0xff&dump_buffer[43]);
	kprintf("Individual Address Register 6 = %02x\r\n", 0xff&dump_buffer[44]);
	
	return OK;
}
